package com.feishi.core.media.javacv;

import org.bytedeco.javacpp.avcodec;
import org.bytedeco.javacv.*;

import javax.sound.sampled.*;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.util.concurrent.*;

import static org.bytedeco.javacpp.FlyCapture2.FRAME_RATE;

/**
 * @version 1.0
 * @auth caixq
 * @time 18-5-9 17:22
 **/
public class VideoCreate implements Runnable{

    private int height;
    private int width;
    private OpenCVFrameGrabber grabber;
    ExecutorService pool=Executors.newCachedThreadPool();
    /**
     * 定时器，用于定时采样音频信息
     * */
    ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
    private FFmpegFrameRecorder recorder;

    @Override
    public void run() {

        pool.execute(()->{

            grabber = new OpenCVFrameGrabber(0);
            boolean openCamera=openCamera(grabber);
            if(!openCamera){
                System.out.println("open camera failed");
            }
            height=grabber.getImageHeight();
            width=grabber.getImageWidth();
            String filepath="/tmp/myvideo.mp4";
            recorder=CreateFrameRecorder(filepath);
            boolean openRecorder=openRecorder(recorder);
            long startTime;
            /**
             * 视频时间戳偏移量
             * */
            long videoTS;
            try {
                Frame capturedFrame = null;
                startTime = System.currentTimeMillis();

                while (true){
                    capturedFrame = grabber.grab();
                    if(recorder==null){
                        continue;
                    }
                    videoTS = 1000 * (System.currentTimeMillis() - startTime);
                    if (videoTS > recorder.getTimestamp()) {
                        //告诉录制器写入这个timestamp
                        recorder.setTimestamp(videoTS);
                        //在此时间戳上插入一帧
                        recorder.record(capturedFrame);
                    }
                }
            } catch (FrameGrabber.Exception e) {
                e.printStackTrace();
            } catch (FrameRecorder.Exception e) {
                e.printStackTrace();
            }

        });
        pool.execute(()->{
            AudioFormat audioFormat = new AudioFormat(44100.0F, 16, 2, true, false);

            TargetDataLine line=getDataLine(audioFormat);
            line.start();
            // 获得当前音频采样率
            int sampleRate = (int) audioFormat.getSampleRate();
            // 获取当前音频通道数量
            int numChannels = audioFormat.getChannels();
            // 初始化音频缓冲区(size是音频采样率*通道数)
            int audioBufferSize = sampleRate * numChannels;
            byte[] audioBytes = new byte[audioBufferSize];
            exec.scheduleAtFixedRate(() -> {
                try {
                    // 非阻塞方式读取
                    synchronized (recorder){
                        if(recorder!=null){

                        }
                        int nBytesRead = line.read(audioBytes, 0, line.available());
                        ShortBuffer sBuff = ByteBuffer.wrap(audioBytes,0,nBytesRead).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
                        recorder.recordSamples(sampleRate, numChannels, sBuff);
                    }
                } catch (FrameRecorder.Exception e) {
                    e.printStackTrace();
                }
            }, 0, (long) 1000 / FRAME_RATE, TimeUnit.MILLISECONDS);
        });

    }

    private TargetDataLine getDataLine(AudioFormat audioFormat) {
        /**
         * 设置音频编码器 最好是系统支持的格式，否则getLine() 会发生错误
         * 采样率:44.1k;采样率位数:16位;立体声(stereo);是否签名;true:
         * big-endian字节顺序,false:little-endian字节顺序(详见:ByteOrder类)
         */

        // 通过AudioSystem获取本地音频混合器信息
        Mixer.Info[] minfoSet = AudioSystem.getMixerInfo();
        // 通过AudioSystem获取本地音频混合器
        Mixer mixer = AudioSystem.getMixer(minfoSet[4]);
        // 通过设置好的音频编解码器获取数据线信息
        DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat);
        // 打开并开始捕获音频
        // 通过line可以获得更多控制权
        // 获取设备：TargetDataLine line
        TargetDataLine line = null;
        try {
            line = (TargetDataLine) mixer.getLine(dataLineInfo);
//          TargetDataLine line = (TargetDataLine) AudioSystem.getLine(dataLineInfo);
            line.open(audioFormat);
            return line;
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        }
        return null;
    }

    private boolean openRecorder(FFmpegFrameRecorder recorder) {
        try {
            recorder.start();
            return true;
        } catch (org.bytedeco.javacv.FrameRecorder.Exception e2) {
            if (recorder != null) {
                try {
                    recorder.stop();
                    recorder.start();
                    return true;
                } catch (org.bytedeco.javacv.FrameRecorder.Exception e) {
                    try {
                        recorder.stop();
                    } catch (org.bytedeco.javacv.FrameRecorder.Exception e1) {
                    }
                }
            }

        }
        return false;
    }

    private FFmpegFrameRecorder CreateFrameRecorder(String filepath) {
        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(filepath, width , height, 2);
        recorder.setInterleaved(true);
        //该参数用于降低延迟
        recorder.setVideoOption("tune", "zerolatency");
        //ultrafast(终极快)提供最少的压缩（低编码器CPU）和最大的视频流大小；而veryslow(非常慢)提供最佳的压缩（高编码器CPU）的同时降低视频流的大小
        recorder.setVideoOption("preset", "ultrafast");
        //设置视频编解码器(codec) -r 25 -设置帧率(frame rate) -b:v 500k -设置视频比特率(bit rate),比特率越高视频越清晰,视频体积也会变大,需要根据实际选择合理范围 -f flv
        recorder.setVideoOption("crf", "25");
        // 2000 kb/s, 720P视频的合理比特率范围
        recorder.setVideoBitrate(2000000);
        // h264编/解码器
        recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
        // 封装格式flv
        recorder.setFormat("flv");
        // 视频帧率(保证视频质量的情况下最低25，低于25会出现闪屏)
        recorder.setFrameRate(FRAME_RATE);
        // 关键帧间隔，一般与帧率相同或者是视频帧率的两倍
        recorder.setGopSize(FRAME_RATE * 2);
        // 不可变(固定)音频比特率
        recorder.setAudioOption("crf", "0");
        // 最高质量
        recorder.setAudioQuality(0);
        // 音频比特率
        recorder.setAudioBitrate(192000);
        // 音频采样率
        recorder.setSampleRate(44100);
        // 双通道(立体声)
        recorder.setAudioChannels(2);
        // 音频编/解码器
        recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
        return recorder;
    }

    private boolean openCamera(OpenCVFrameGrabber grabber) {
        try {
            grabber.start();
            return true;
        } catch (org.bytedeco.javacv.FrameGrabber.Exception e2) {
            if (grabber != null) {
                try {
                    grabber.restart();
                    return true;
                } catch (org.bytedeco.javacv.FrameGrabber.Exception e) {
                    try {
                        grabber.stop();
                    } catch (org.bytedeco.javacv.FrameGrabber.Exception e1) {
                    }finally {
                        return false;
                    }
                }
            }
        }
        return false;
    }
}